import datetime
import json
import time
from django.conf import settings
from basics import models as basics_models
from basics.serializers import CasesSerializers
from ApiTest import models as api_models
from ApiTest import serializers as api_serializers
from ProjectTool.ApiTool.parsing_data import ParsingData
from WEBTest import models as web_models
from ProjectTool.WebTool.ParsingStepsWebData import ParsingStepsWebData
from ProjectTool.common_tool.data_tool.file_uploaded import FileUploaded
from ProjectTool.common_tool.data_tool.get_query_dict import GetQueryDict
from ProjectTool.common_tool.data_tool.save_sql_detail import SaveSqlDetail
from ProjectTool.common_tool.in_out_parameter import FuncParameter, StepsData
from utils.base_response import BaseResponse
from concurrent.futures import wait, ALL_COMPLETED,ThreadPoolExecutor

# THREAD_POOL = ThreadPoolExecutor(settings.THREAD_POOL_COUNT)  # 线程池


class ExecuteCasesTool:

    def __init__(self, cases_ids, driver_status=None):
        self.cases_ids = cases_ids
        self.driver_status = driver_status  # 是否使用无界面浏览器 True:使用   False：不使用
        self.cases_query_set = None  # 用例数据
        self.cases_detail_querys = {}  # 用例与步骤数据
        self.gevent_list = []  # 协程任务列表
        self.thread_pool = ThreadPoolExecutor(settings.THREAD_POOL_COUNT)  # 线程池  # 线程池
        self.exe_number = 3  # 用例步骤执行失败时可再次执行的次数
        self.detail_objs = [
            (api_models.ApiDetail, self.execute_api_detail),  # 接口明细步骤
            (web_models.Steps, self.execute_web_detail),  # 接口明细步骤
        ]
        self.res_data = {
            'cases_sum': 0,  # 用例总条数
            'cases_execute_sum': 0,  # 用例执行条数
            'cases_failure_sum': 0,  # 用例失败条数
            'cases_not_execute_sum': 0,  # 用例未执行条数
            'cases_failure_ids': [],  # 用例失败id列表
            'cases_detail_sum': 0,  # 明细总条数
            'cases_detail_execute_sum': 0,  # 明细执行条数
            'cases_detail_failure_sum': 0,  # 明细失败条数
            'cases_detail_not_execute_sum': 0,  # 明细未执行条数
            'cases_detail_failure_ids': [],  # 明细失败id列表
        }

    # 用例数据初始化
    def get_cases_query_set(self):
        query_set = basics_models.Cases.objects.filter(id__in=self.cases_ids, status=1)
        if query_set.count():
            query_set.update(
                test_status=3,
                exe_number=0
            )
            self.cases_query_set = query_set.order_by('project_id', 'module_id', 'id')

    # 用例步骤初始化
    def get_cases_detail_query_set(self):
        for cases_query in self.cases_query_set:
            # 获取步骤
            cases_detail_query = cases_query.casesdetail_set.filter(status=1).order_by('sort')
            if cases_detail_query.count():
                cases_detail_query.update(
                    test_status=3,
                    results=3,
                    error_info=None,
                    steps_data=None
                )
                for detail_query in cases_detail_query:
                    content_object = detail_query.content_object
                    content_object.results = 3
                    content_object.save()
                self.cases_detail_querys[cases_query] = cases_detail_query

    # 接口明细步骤
    def execute_api_detail(self, queryset, steps_data, data_warehouse, driver=None, cases_status=True):
        '''
        执行接口测试，分发处
        :param queryset: 步骤实现的queryset
        :param data_warehouse: 数据仓库
        :return:
        '''
        obj = ParsingData(
            serializer=api_serializers.ApiDetailSerializers,
            queryset=queryset,
            data_warehouse=data_warehouse,
            steps_data=steps_data,
            cases_status=cases_status
        )
        obj.run()
        res = obj.res
        data_warehouse = obj.data_warehouse  # 数据仓库
        results = obj.test_data['results']
        return res, data_warehouse, driver, results

    # WEB明细步骤
    def execute_web_detail(self, queryset, steps_data, data_warehouse, driver=None, cases_status=True):
        '''
        执行接口测试，分发处
        :param queryset: 步骤实现的queryset
        :param data_warehouse: 数据仓库
        :return:
        '''
        obj = ParsingStepsWebData(
            queryset=queryset,
            steps_data=steps_data,
            driver=driver,
            driver_status=self.driver_status,
            data_warehouse=data_warehouse,
            cases_status=cases_status
        )
        obj.run()
        res = obj.res
        driver = obj.driver
        results = obj.results
        return res, data_warehouse, driver, results

    # 数据处理
    def get_parsing_data(self, out_data, data_warehouse):
        res = BaseResponse()
        if out_data:
            func_obj = FuncParameter(out_data)
            func_obj.run()
            out_data = func_obj.obj_data
            if func_obj.res.status:
                res = func_obj.res
            if not res.status:
                try:
                    out_data = json.loads(out_data) if out_data else {}
                except Exception as e:
                    res.status = 1
                    res.msg = '数据转行字典失败！'
                    res.error = str(e.args)
            if not res.status:
                out_obj = StepsData(out_data, data_warehouse, None)
                out_obj.run()
                out_data = out_obj.out_data
                if out_obj.res.status:
                    res = out_obj.res
        return out_data, res

    # 以用例为key步骤为value
    def steps_distribute(self, detail_query, data_warehouse=None, driver=None):
        # 步骤分发
        queryset = detail_query.content_object  # 对应表的数据
        steps_data_template = detail_query.steps_data_template  # 步骤数据模板
        steps_data, res = self.get_parsing_data(steps_data_template, data_warehouse)
        results = 3  # 步骤执行状态
        status = False  # 是否找到对应的函数执行步骤
        if not res.status:
            for detail in self.detail_objs:
                if isinstance(queryset, detail[0]):
                    func = detail[1]
                    try:
                        func_res, data_warehouse, driver, results = func(queryset, steps_data, data_warehouse, driver)
                        if func_res.status:
                            res = func_res
                    except Exception as e:
                        res.status = 1
                        res.msg = '步骤执行失败！'
                        res.error = str(e.args)
                        results = 4
                        # raise e
                    status = True
                    break
            if not status:
                res.status = 1
                res.msg = '该步骤找不到对应的执行函数！'
                res.data = None
                results = 4
        else:
            results = 4
        return res, data_warehouse, driver, results, steps_data

    # 执行用例步骤
    def execute_cases_detail(self, cases_query, cases_detail_lis):
        data_warehouse = {}
        cases_test_status = 1  # 用例的测试结果
        decimal_digits = 6  # 执行时间保留的小数位数
        exe_count = 0  # 执行次数
        drivers = []  # 浏览器对象
        cases_start_time = time.time()  # 用例执行起始时间
        for i in range(self.exe_number):
            exe_count += 1
            driver_old = None
            cases_detail_execute_sum = 0  # 用例明细执行次数
            cases_detail_failure_sum = 0  # 用例明细执行失败次数
            cases_detail_failure_ids = []  # 用例明细执执行失败id
            for cases_detail in cases_detail_lis:  # 遍历步骤
                assert_status = cases_detail.assert_status  # 获取步骤中的断言状态
                cases_detail_execute_sum = cases_detail_execute_sum + 1
                cases_detail_start_time = time.time()
                res, data_warehouse, driver_new, results, steps_data = self.steps_distribute(cases_detail,
                                                                                         data_warehouse,
                                                                                         driver=driver_old)  # 步骤分发
                if driver_old is not driver_new:
                    drivers.append(driver_new)
                    driver_old = driver_new
                cases_detail_stop_time = time.time()
                if results is not assert_status:  # 当执行结果与断言结果不一致时测试失败
                    test_status = 2  # 步骤测试状态
                else:
                    test_status = 1  # 步骤测试状态
                cases_detail_execution_time = datetime.datetime.now()  # 步骤执行时间
                cases_detail_consume_time = cases_detail_stop_time - cases_detail_start_time  # 步骤耗时
                cases_detail.execution_time = cases_detail_execution_time
                cases_detail.consume_time = round(cases_detail_consume_time, decimal_digits)
                cases_detail.results = results
                cases_detail.test_status = test_status
                cases_detail.steps_data = steps_data
                cases_detail.error_info = json.dumps(res.dict, ensure_ascii=False) if res.status else None
                cases_detail.save()
                if test_status is 2:  # 判断是否执行失败
                    cases_detail_failure_sum = cases_detail_failure_sum + 1
                    cases_detail_failure_ids.append(cases_detail.id)
            for driver in drivers:
                driver.quit()
            drivers = []
            if not cases_detail_failure_ids: break

        self.res_data['cases_detail_execute_sum'] += cases_detail_execute_sum
        self.res_data['cases_detail_failure_sum'] += cases_detail_failure_sum
        self.res_data['cases_detail_failure_ids'] += cases_detail_failure_ids
        if cases_detail_failure_ids:
            self.res_data['cases_failure_sum'] = self.res_data['cases_failure_sum'] + 1
            self.res_data['cases_failure_ids'].append(cases_query.id)
            cases_test_status = 2
        cases_stop_time = time.time()  # 用例执行结束时间
        cases_execution_time = datetime.datetime.now()  # 执行时间
        cases_consume_time = cases_stop_time - cases_start_time  # 用例耗时
        if cases_test_status is not 3:
            self.res_data['cases_execute_sum'] = self.res_data['cases_execute_sum'] + 1
        query_dict = GetQueryDict().get_query()
        file_name = ''.join([
            'warehouse_data',
            '_%s_' % cases_query.id,
            cases_execution_time.strftime('%Y-%m-%d %H-%M-%S'),
            '.txt'])
        obj = FileUploaded(data_warehouse, file_name).get_uploaded_file()
        query_dict.__setitem__('warehouse_data', obj)  # 数据仓库写入
        query_dict.__setitem__('test_status', cases_test_status)  # 用例的测试结果
        query_dict.__setitem__('consume_time', round(cases_consume_time, decimal_digits))  # 用例耗时
        query_dict.__setitem__('execution_time', cases_execution_time)  # 执行时间
        query_dict.__setitem__('exe_number', exe_count)  # 执行次数
        sql_obj = SaveSqlDetail(CasesSerializers, cases_query, query_dict)  # 主要保存数据仓库数据
        sql_obj.save()

    # 执行用例
    def execute_cases(self):
        all_task = []
        self.res_data['cases_sum'] = self.res_data['cases_sum'] + len(self.cases_detail_querys)
        for cases_query in self.cases_detail_querys:  # 遍历用例，此处条件成熟时可考虑实现并发
            cases_detail_lis = self.cases_detail_querys[cases_query]  # 获取用例下的步骤
            self.res_data['cases_detail_sum'] = self.res_data['cases_detail_sum'] + len(cases_detail_lis)
            # self.execute_cases_detail(cases_query, cases_detail_lis)
            task = self.thread_pool.submit(self.execute_cases_detail, cases_query, cases_detail_lis)
            all_task.append(task)
        # self.thread_pool.shutdown(True)  # 关闭提交通道
        wait(all_task, return_when=ALL_COMPLETED)  # 等待此次执行完毕
        self.res_data['cases_not_execute_sum'] = self.res_data['cases_sum'] - self.res_data['cases_execute_sum']
        self.res_data['cases_detail_not_execute_sum'] = self.res_data['cases_detail_sum'] - self.res_data[
            'cases_detail_execute_sum']

    def run(self):
        self.get_cases_query_set()
        self.get_cases_detail_query_set()
        self.execute_cases()
        return self.res_data
